using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace ProgramLogger
{
    public class LogFile
    {
        /** PUBLIC GLOBAL VARS **/
        public enum LogType 
        {
            TXT, 
            HTML_Plain
        };

        [Flags]
        public enum LogLevel 
        {
            Debug =  0x01, 
            Info  =  0x02, 
            Warn  =  0x04, 
            Error =  0x08,
            User1 =  0x10,
            User2 =  0x20,
            All   =  0xFF
        };

        /** PRIVATE GLOBAL VARS *****/
        private StreamWriter Writer;
        private bool LogFileIsOpen;
        private LogType Type;
        private LogLevel Level;

        // Constructor 1: Init class without log-file
        public LogFile()
        {
            LogFileIsOpen = false;
        }

        // Contructor 2: Init class with log-file, try to open log-file
        // for write-access. If an error occurs throw it to the caller.

        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public LogFile(string filename, bool append, LogType typ)
        {
            Open(filename, append, typ);
        }

        // Contructor 3: Init class with log-file, try to open log-file
        // for write-access and with sets Log-Level. If an error occurs 
        // throw it to the caller.

        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public LogFile(string filename, bool append, LogType typ, LogLevel level)
        {
            Open(filename, append, typ, level);
        }

        // Contructor 3: Init class with log-file, try to open log-file
        // for write-access and with sets Log-Level. Furthermore you
        // can set an Title. If an error occurs throw it to the caller.

        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public LogFile(string filename, bool append, LogType typ, LogLevel level, string title)
        {
            Open(filename, append, typ, level, title);
        }


        // destructor: close log-file
        ~LogFile()
        {
            this.Close();
        }

        // FUNCTION Open(): open new log-file with level
        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public void Open(string filename, bool append, LogType typ, LogLevel level, string title)
        {
            // at first --> close open log-file (if necessary)
            this.Close();

            // create new log-file
            // If an error occurs throw it to the caller.
            Writer = new StreamWriter(filename, append, Encoding.UTF8);
            LogFileIsOpen = true;
            Type = typ;
            Level = level;

            // if type=HTML write header
            if (typ == LogType.HTML_Plain)
            {
                LogRaw("<?xml version='1.0' ?>");
                LogRaw("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
                LogRaw("<html xmlns='http://www.w3.org/1999/xhtml'>");
                LogRaw("<head>");
                if(title == "")
                    LogRaw("<title>Logfile " + DateTime.Today.ToShortDateString() + "</title>");
                else
                    LogRaw("<title>" + title + "</title>");
                LogRaw("<style type='text/css'>body{font-family:monospace;}</style>");
                LogRaw("</head><body>");
            }
            // if type=TXT write title
            else if(typ == LogType.TXT)
            {
               if(title != "") LogRaw(title);
            }
        }

        // FUNCTION Open(): open new log-file without level and title (Level is set to All)
        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public void Open(string filename, bool append, LogType typ)
        {
            Open(filename, append, typ, LogLevel.All, "");
        }

        // FUNCTION Open(): open new log-file without title (is set to "" )
        ///<exception cref="System.IO.IOException"> ;
        ///IOException
        ///<see cref="ExceptionHandling" />
        ///</exception>
        public void Open(string filename, bool append, LogType typ, LogLevel level)
        {
            Open(filename, append, typ, level, "");
        }

        // FUNCTION Close() : close log-file if neccessary
        public void Close()
        {
            // at first check if a log-file is open, then close it!
            try
            {
                if (LogFileIsOpen)
                {
                    // if type is HTML end it
                    if (this.Type == LogType.HTML_Plain)
                    {
                        LogRaw("</body></html>");
                    }

                    Writer.Close();
                    Writer.Dispose();
                    LogFileIsOpen = false;                   
                }

            }
            // ignore exception
            catch (Exception)
            {
                LogFileIsOpen = false;
            }
        }

        // FUNKTION LogRaw() : write plain-log-line to file (without special-formatting)
        public void LogRaw(string text)
        {
            Writer.WriteLine(text);
            Writer.Flush();
        }

        // FUNKTION Log() : write log-line to file
        public void Log(string text, LogLevel level)
        {
            // Check Level
            if ((level & this.Level) == 0) return;

            // format pre-string
            string prestring;
            switch (level)
            {
                case (LogLevel.Debug):  prestring = "DEBUG " + DateTime.Now.ToString() + " "; break;
                case (LogLevel.Info):   prestring = "INFO  " + DateTime.Now.ToString() + " "; break;
                case (LogLevel.Warn):   prestring = "WARN  " + DateTime.Now.ToString() + " "; break;
                case (LogLevel.Error):  prestring = "ERROR " + DateTime.Now.ToString() + " "; break;
                default:                prestring = ""; break;
            }

            // format text depening on type
            string formatted_text;
            switch (this.Type)
            {
                // HTML_Plain
                case LogType.HTML_Plain: formatted_text = prestring + text + "<br/>"; break;

                // PLAINTEXT
                default: formatted_text = text; break;
            }

            // write to file and flush
            Writer.WriteLine(formatted_text);
            Writer.Flush();
        }

        // DEPRECATED: FUNKTION Log() : write plain-log-line to file 
        public void Log(string text)
        {
            Log(text, LogLevel.All);
        }

        // DEPRECATED: FUNCTION LogError() : write error-log-line to file
        public void LogError(string text)
        {
            Log(text, LogLevel.Error);
        }

        // DEPRECATED: FUNCTION LogInfo() : write info-log-line to file
        public void LogInfo(string text)
        {
            Log(text, LogLevel.Info);
        }

        // PROPERTY Version : return program-version (read-only)
        public Version Version
        {
            get
            {
                return (System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
            }
        }

        // PROPERTY IsOpen : return if a LogFile is already open (read-only)
        public bool IsOpen
        {
            get
            {
                return LogFileIsOpen;
            }
        }

        // PROPERTY CurrentLogType : return current LogType (read-only)
        public LogType CurrentLogType
        {
            get
            {
                return Type;
            }
        }

        // PROPERTY CurrentLogLevel : return current LogType (read/write)
        public LogLevel CurrentLogLevel
        {
            get
            {
                return Level;
            }
            set
            {
                Level = value;
            }
        }
    }
}
